home *** CD-ROM | disk | FTP | other *** search
- /* This file contains gravity effect routines
-
- Data format of gravity_map is:
-
- short 'width'
- short 'height'
-
- height times:
-
- { int 'offset to scanline from here' }
-
- height times:
-
- {
- for a line containing nothing:
-
- {
- short 'width'
- }
-
- or for a line containing something:
-
- {
- short 'leading zeros'
- short 'writes'
- writes times: short 'offset of source pixel'
- }
- }
- */
-
- #include "stdafx.h"
-
- #include <math.h>
-
- static short *radial_gravity(int maxr, double a, double t, double f(double r, double a, double t), int &n)
- {
- short w = 2 * maxr - 1, *gravity_map = new short [2 + 3 * w + square(w)], *g = gravity_map;
-
- // Store size of gravity map
-
- *g++ = w;
- *g++ = w;
-
- // Make room for table to store line offsets
-
- int *current_line = (int *)g;
-
- g = (short *)¤t_line[w];
-
- // Compute the gravity map
-
- for (int y = -maxr + 1; y < maxr; y++)
- {
- short lead_zeros = 0, writes = 0, *where_writes, trail_zeros = 0, non_zero = FALSE;
- int offset;
-
- // Store location of line
-
- *current_line++ = (int)g - (int)current_line;
-
- // Create gravity table
-
- for (int x = -maxr + 1; x < maxr; x++)
- {
- double r = sqrt(square(x) + square(y));
-
- if (r == 0 || r > maxr - 1)
- offset = 0;
- else
- {
- // Compute displacement in x and y direction
-
- double v = f(r / maxr, a, t), gx = v * x / r, gy = v * y / r;
-
- // Round
-
- gx = gx < 0? gx - 0.5 : gx + 0.5;
- gy = gy < 0? gy - 0.5 : gy + 0.5;
-
- // Clip
-
- if (x + gx <= -maxr)
- gx = -maxr + 1 - x;
- else if (x + gx >= maxr)
- gx = maxr - 1 - x;
-
- if (y + gy <= -maxr)
- gy = -maxr + 1 - y;
- else if (y + gy >= maxr)
- gy = maxr - 1 - y;
-
- // Compute offset
-
- offset = (int)gx + (int)gy * w;
-
- ASSERT(offset == (short)offset);
- }
-
- if (offset == 0)
- {
- // No change for this pixel
-
- if (non_zero)
- {
- // Non zero occured, increase trailing zeros
-
- trail_zeros++;
-
- // Store zero in case there are non zero offsets following
-
- *g++ = offset;
- writes++;
- }
- else
- {
- // Only zeros yet, increase number of zeros at start
-
- lead_zeros++;
- }
- }
- else if (!non_zero)
- {
- // First non zero offset, store number of starting zeros
-
- *g++ = lead_zeros;
-
- // Make room for the number of bytes to write
-
- where_writes = g;
- g++;
-
- // Store first non zero offset
-
- *g++ = offset;
- writes++;
-
- // Indicate that non zero was encountered
-
- non_zero = TRUE;
- }
- else
- {
- // Offset is non zero and at least one non zero offset before,
- // reset trailing zeros
-
- trail_zeros = 0;
-
- // Store value
-
- *g++ = offset;
- writes++;
- }
- }
-
- if (writes != 0)
- {
- // Store number of bytes to write
-
- *where_writes = writes - trail_zeros;
-
- // Remove trailing zeros from this line
-
- g -= trail_zeros;
- }
- else
- {
- // Indicate that nothing has to be written
-
- *g++ = w;
- }
- }
-
- // Store bytes written
-
- n = (int)g - (int)gravity_map;
-
- // Return buffer
-
- return gravity_map;
- }
-
- static double f_ripple(double r, double a, double t)
- {
- return a * (1 - r) * cos(4.5 * PI * r + 2 * PI * t);
- }
-
- static double f_sphere(double r, double a, double t)
- {
- return -a * r / sqrt(1 - r * r);
- }
-
- void create_gravity()
- {
- short *g;
- int n;
-
- // Create ripple effect
-
- for (int t = 0; t < 6; t++)
- {
- g = radial_gravity(150, 20, (double)t / 6, f_ripple, n);
- cData::add_to_file("Gravity.dat", g, n, construct("RIPPLE%d", t + 1));
- delete g;
- }
-
- // Create spheres
-
- g = radial_gravity(40, 3, 0, f_sphere, n);
- cData::add_to_file("Gravity.dat", g, n, "SPHERE40");
- delete g;
-
- g = radial_gravity(60, 5, 0, f_sphere, n);
- cData::add_to_file("Gravity.dat", g, n, "SPHERE60");
- delete g;
-
- g = radial_gravity(80, 7, 0, f_sphere, n);
- cData::add_to_file("Gravity.dat", g, n, "SPHERE80");
- delete g;
-
- g = radial_gravity(100, 10, 0, f_sphere, n);
- cData::add_to_file("Gravity.dat", g, n, "SPHERE100");
- delete g;
- }
-
- void gravity_blit(LPDIRECTDRAWSURFACE4 dest, RECT *destrect, int x, int y, short *gravity_map)
- {
- BYTE *surface_copy, *s, *d;
- DDSURFACEDESC2 destsurf;
- int rw, rh, w, h, max_x, max_y;
-
- // Get size of rectangle
-
- rw = destrect->right - destrect->left;
- rh = destrect->bottom - destrect->top;
-
- // Get size of image
-
- w = *gravity_map++;
- h = *gravity_map++;
-
- ASSERT(rw >= w && rh >= h);
-
- // Check if there will be anything to blit
-
- if (x <= -w || x >= rw || y <= -h || y >= rh)
- return;
-
- // Compute distance from x to right of rectangle with maximum w
-
- max_x = rw - x;
-
- if (max_x > w)
- max_x = w;
-
- // Compute distance from y to bottom of rectangle with maximum h
-
- max_y = rh - y;
-
- if (max_y > h)
- max_y = h;
-
- // Allocate temporary space
-
- surface_copy = new BYTE [w * h];
-
- // Lock surface
-
- destsurf.dwSize = sizeof(destsurf);
-
- while (!draw_ok(dest->Lock(destrect, &destsurf, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, 0)));
-
- // Fill surface_copy buffer entirely (if not with image data then fill with black)
-
- s = (BYTE *)destsurf.lpSurface + (x > 0? x : 0) + y * destsurf.lPitch;
- d = surface_copy;
-
- for (int i = 0; i < h; i++)
- {
- if (i < -y || i >= max_y)
- {
- FillMemory(d, w, black);
- }
- else
- {
- if (x >= 0)
- {
- CopyMemory(d, s, max_x);
-
- if (max_x < w)
- FillMemory(d + max_x, w - max_x, black);
- }
- else // if (x < 0)
- {
- FillMemory(d, -x, black);
- CopyMemory(d - x, s, w + x);
- }
- }
-
- s += destsurf.lPitch;
- d += w;
- }
-
- // Setup source and destination pointer
-
- s = surface_copy + (x < 0? -x : 0) + (y < 0? -y * w : 0);
- d = (BYTE *)destsurf.lpSurface + (x > 0? x : 0) + (y > 0? y * destsurf.lPitch : 0);
-
- // Go to the right offset value in the gravity map
-
- if (y < 0)
- gravity_map = (short *)((int *)gravity_map - y);
-
- // Setup some values required in the assembly part
-
- int write_x_start = x < 0? -x : 0,
- write_y = y < 0? h + y : max_y;
-
- // Do the blit
-
- __asm
- {
- mov esi, gravity_map
-
- mov ebx, s
- mov edi, d
-
- mov ecx, write_y
- blit_loop_y:
-
- // Get start of this scanline in the gravity map
-
- push esi
- add esi, [esi]
-
- // Get number of leading zeros
-
- lodsw
- cwde
-
- // Check leading zeros >= max_x
-
- cmp eax, max_x
- jge line_empty
-
- // Store registers
-
- push ebx
- push ecx
- push edi
-
- // Get pixels to skip
-
- sub eax, write_x_start
- jc skip_more_than_leading
-
- // Case 1: There are less pixels to skip than the number of leading zeros
-
- // Get the remaining number of pixels to be written in ecx
-
- mov ecx, max_x
- sub ecx, write_x_start
- sub ecx, eax
- jbe line_empty2
-
- // Adjust pointers to first pixel that has to be written
-
- add ebx, eax
- add edi, eax
-
- // Get number of pixels writable in this line
-
- lodsw
- cwde
-
- // Take the least of the two counters
-
- cmp eax, ecx
- jge blit_loop_x
- mov ecx, eax
- jmp blit_loop_x
-
- skip_more_than_leading:
-
- // Case 2: There are more pixels to skip than the number of leading zeros
-
- // Get the number of pixels that still have to be skipped in edx
-
- neg eax
- mov edx, eax
-
- // Get number of pixels writable in this line
-
- lodsw
- cwde
-
- // Get the remaining number of pixels to be written in ecx
-
- mov ecx, eax
- sub ecx, edx
- jbe line_empty2
-
- // Adjust the gravity map pointer
-
- shl edx, 1
- add esi, edx
-
- // Enter loop
-
- blit_loop_x:
-
- // Get offset
-
- lodsw
- cwde
-
- // Get pixel from offset location and store it to the current location
-
- mov dl, [ebx + eax]
- mov [edi], dl
-
- // Goto next pixel
-
- inc ebx
- inc edi
-
- loop blit_loop_x
-
- line_empty2:
-
- // Restore registers
-
- pop edi
- pop ecx
- pop ebx
-
- line_empty:
-
- // Go to next line
-
- add ebx, w
- add edi, destsurf.lPitch
-
- pop esi
- add esi, 4
-
- loop blit_loop_y
- }
-
- // Unlock surface
-
- backbuffer->Unlock(destrect);
-
- // Release temporary buffer
-
- delete surface_copy;
- }
-
- static void write_all_gravity(cDisplayable *l)
- {
- if (!no_parallax && !inawin && !low_detail_level)
- for (; l != 0; l = (cDisplayable *)l->next)
- l->write_gravity();
- }
-
- void write_gravity()
- {
- write_all_gravity(weapons);
- write_all_gravity(effects);
- }